package fregat.connector.worker;

import fregat.base.database.DatabaseConnector;
import fregat.base.util.StopWatch;
import fregat.connector.ConnectorService;

import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Enumeration;

/**
 * Worker that implements the iphost receive logic.
 *
 * @author Alexander Mann <br><b><i>ECO_L O G</i> &nbsp Logistiksysteme GmbH</b>
 * @version 1.0
 */
public class ReceiveWorker extends DatabaseWorker {
  private int quitLevel; // level for quit telegram creation

  /**
   * Creates a new receive worker, which has the following logic.
   * <li>waits until data is received on the
   * corresponding channel(s)</li>
   * <li>opens the configured database connection and
   * inserts the data executing the configured insert procedure</li>
   * <li>if configured a first level quit telegram is created exectuing the
   * configured quit procedure and sent to communication partner</li>
   * <li>the process procedure is executed to process the data in database</li>
   * <li>if configured a second level quit telegram is created and sent</li>
   *
   * @param name Configuration name of worker
   */
  public ReceiveWorker(String name) {
    super(name);
    String configPath = "worker.config." + getName();

    // configure quit level -->
    Enumeration e = configuration.getConfigurationEnumeration(configPath + ".quit", ",", configuration.INTEGER);
    while (e.hasMoreElements()) {
      quitLevel += ((Integer) e.nextElement()).intValue();
    }
    // <--

    if (insertSQL == null) {
      // data will be ignored if no 'insert' function configured
      ConnectorService.error(ConnectorService.resource
          .getString("ReceiveWorker_constructor_1")
          .replaceFirst("%1", getName()));
    }

    if (processSQL == null) {
      // data will not be processed if no 'process' function configured
      ConnectorService.warn(ConnectorService.resource
          .getString("ReceiveWorker_constructor_2")
          .replaceFirst("%1", getName()));
    }
  }

  /**
   * Transfers received data to the database and processes it. If configured
   * the quit telegrams are created and sent to the communication partner on the
   * channel, where the data was received.
   *
   * @param channelName  Configuration name of channel
   * @param receivedData Data that was received
   */
  protected synchronized boolean dataReceived(String channelName, String receivedData) {
    StopWatch.resetTime("receive");

    boolean receiveOk = super.dataReceived(channelName, receivedData);

    if (!databaseConnector.isConnected()) {
      databaseConnector.connectToDatabase();
      databaseConnector.setName(ConnectorService.VERSION);
      databaseConnector.setAction("Receive");
    }

    long sstId;

    if (insertSQL != null) {
      try {
        // insert into database
        sstId = insertData(channelName, receivedData);

        if (sstId == 0) {
          // data was inserted but should not be processed or acknowledged(quit)
          databaseConnector.commit();

        } else if (sstId > 0) {
          // data was inserted and has to be processed
          databaseConnector.commit();

          // first level quit (data received)
          if (quitSQL != null && (quitLevel == 1 || quitLevel == 3)) {
            processQuit(sstId, 1);
          }

          // process data
          if (processSQL != null) {
            processData(sstId, null);

            // commit changes to database
            databaseConnector.commit();
          }

          // second level quit (data processed)
          if (quitSQL != null && (quitLevel >= 2)) {
            processQuit(sstId, 2);
          }

        } else {
          // data was not inserted
          databaseConnector.rollback();
        }
      } catch (SQLException ex) {
        databaseConnector.rollback();
        ConnectorService.error(getName() + ", ReceiveWorker.dataReceived", ex);
        receiveOk = false;
      }
    }

    if(receiveOk) {
      ConnectorService.debug(getName() + ", " + ConnectorService.resource.getString("ReceiveWorker_dataReceived_1") +
          " " + StopWatch.takeTime("receive") + "[ms]");
    }

    if (shutdown) {
      databaseConnector.shutdown();
    }

    return receiveOk;
  }

  /**
   * Executes the quit function in database and sends the quit telegram if
   * available.
   *
   * @param sstId ID of data that quit has to be created for
   * @param level Quit level (1,2)
   * @throws SQLException
   */
  private void processQuit(long sstId, int level) throws SQLException {
    CallableStatement statement = databaseConnector.getStatement(quitKey);
    if (statement == null) {
      statement = databaseConnector.prepareStatement(quitKey,
          quitSQL);
      statement.registerOutParameter(1, Types.INTEGER);
      statement.registerOutParameter(4, Types.VARCHAR);
    }

    statement.setLong(2, sstId);
    statement.setInt(3, level);

    executeStatement(DatabaseConnector.logStatement(quitKey, new String[]{Long.toString(sstId), Integer.toString(level), ""}),
        quitKey);

    databaseConnector.commit();

    if (statement.getLong(1) >= 0) {
      // send quit telegram on receive channel
      send(channelName, statement.getString(4));
    }
  }
}
